package com.github.llyb120.calengine.rule;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.github.llyb120.calengine.method.*;
import com.github.llyb120.performance.define.*;

import javax.script.*;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;


public class RuleEngine {

    private ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");
    private Bindings bingdings = engine.createBindings();
//    private Map<String, List<AbstractRule>> cache = new HashMap<>();
    private Map<String, Map<String, Object>> indexes = new ConcurrentHashMap<>();
    public final static String GENERATOR = "generator";
    public final static String DATASOURCE = "datasource";
    public final static String CONVERTER = "converter";
    public final static String DATASET = "dataset";
    public final static String SCOPE = "scope";
    public final static String QUOTA = "quota";

    private final static String path = "D:\\work\\cal-engine\\demo\\define";



    public RuleEngine(){
        indexes.put(GENERATOR, new ConcurrentHashMap<>());
        indexes.put(DATASOURCE, new ConcurrentHashMap<>());
        indexes.put(CONVERTER, new ConcurrentHashMap<>());
        indexes.put(DATASET, new ConcurrentHashMap<>());
        indexes.put(SCOPE, new ConcurrentHashMap<>());
        indexes.put(QUOTA, new ConcurrentHashMap<>());

        init();
    }

    public void init(){
        engine.setBindings(bingdings, ScriptContext.ENGINE_SCOPE);
        loadScripts();

        //加载指标
        for (Object rule : indexes.get(QUOTA).values()) {
            Quota quota = (Quota) rule;
            bingdings.put(quota.getName().toUpperCase(Locale.ROOT), quota.getCalculate());
        }

        //绑定方法
        bingdings.put(toScriptName(FromScope.class), new FromScope(this));
        bingdings.put(toScriptName(ExposeFunc.class), new ExposeFunc(this));
        bingdings.put(toScriptName(DrawTableFunc.class), new DrawTableFunc(this));
        bingdings.put(toScriptName(DrawPieFunc.class), new DrawPieFunc(this));
        bingdings.put(toScriptName(CalculateEachFunc.class), new CalculateEachFunc(this));


    }

    public ScriptEngine getScriptEngine(){
        return engine;
    }

    public DatasetWrapper getDataset(String name){
        try{
            return (DatasetWrapper) indexes.get(DATASET).get(name);
        } catch (Exception e){
            return null;
        }
    }

    public DatasourceWrapper getDatasource(String name){
        try{
            return (DatasourceWrapper) indexes.get(DATASOURCE).get(name);
        } catch (Exception e){
            return null;
        }
    }

    public QuotaWrapper getQuota(String name){
        try{
            return (QuotaWrapper) indexes.get(QUOTA).get(name);
        } catch (Exception e){
            return null;
        }
    }


    public ConverterWrapper getConverter(String name){
        try{
            return (ConverterWrapper) indexes.get(CONVERTER).get(name);
        } catch (Exception e){
            return null;
        }
    }

    public GeneratorWrapper getGenerator(String name){
        try{
            return (GeneratorWrapper) indexes.get(GENERATOR).get(name);
        } catch (Exception e){
            return null;
        }
    }


//    public <T> T getRule(String ruleType, String ruleName){
//        AbstractRule rule = indexes.get(ruleType).get(ruleName);
//        if (rule == null) {
//            return null;
//        }
//        return (T) rule.getRule();
//    }


    public Object runCalculateScript(String code){
        try {
            return engine.eval(code, bingdings);
        } catch (ScriptException e) {
            e.printStackTrace();
            return null;
        }
    }


    private void loadSingleScript(Path path){
        try{
            engine.eval(FileUtil.readUtf8String(path.toFile()), bingdings);
            for (Object e : Helper.GetElements()) {
                String key = null;
                String name = null;
                Object item = null;
                if(e instanceof Generator){
                    key = GENERATOR;
                    item = new GeneratorWrapper(this, (Generator) e);
                    name = ((Generator) e).getName();
                } else if(e instanceof Datasource){
                    key = DATASOURCE;
                    item = new DatasourceWrapper(this, (Datasource) e);
                    name = ((Datasource) e).getName();
                } else if(e instanceof Dataset){
                    key = DATASET;
                    item = new DatasetWrapper(this, (Dataset) e);
                    name = ((Dataset) e).getName();
                } else if(e instanceof Converter){
                    key = CONVERTER;
                    item = new ConverterWrapper(this, (Converter) e);
                    name = ((Converter) e).getName();
                } else if(e instanceof Quota){
                    key = QUOTA;
                    item = new QuotaWrapper(this, (Quota) e);
                    ((Quota) e).setName(((Quota) e).getName().toUpperCase(Locale.ROOT));
                    name = ((Quota) e).getName();
                }
                if (key != null && name != null && item != null) {
                    indexes.get(key).put(name, item);
                }
            }
        } catch (Exception e){
            e.printStackTrace();
            System.out.println("加载脚本失败");
        } finally {
            Helper.ClearElements();
        }
    }

    private void loadScripts(){
        FileUtil.walkFiles(Paths.get(path), 10, new SimpleFileVisitor<Path>(){
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                scan:
                if(!file.getFileName().endsWith(".groovy")){
                    break scan;
                }
                loadSingleScript(file);
                return super.visitFile(file, attrs);
            }
        });
    }


    private String toScriptName(String name){
        return StrUtil.toUnderlineCase(name).toUpperCase(Locale.ROOT);
    }
    private String toScriptName(Class clz){
        String name = clz.getSimpleName();
        if(name.endsWith("Func")){
            name = name.substring(0, name.length() - "Func".length());
        }
        return toScriptName(name);
    }


}
